package asem.ast.sentencias;

import genc.GenC;
import alex.Token;
import asem.TablaSimbolos;
import asem.ast.expresiones.NodoExpresion;
import asem.excepciones.ExcepcionAccesoInvalido;
import asem.excepciones.ExcepcionEntidadNoDeclarada;
import asem.excepciones.ExcepcionTiposIncompatibles;
import asem.tablaSimbolos.AtributoDeInstancia;
import asem.tablaSimbolos.Constructor;
import asem.tablaSimbolos.Metodo;
import asem.tablaSimbolos.Parametro;
import asem.tablaSimbolos.Variable;
import asem.tablaSimbolos.VariableLocal;
import asem.tablaSimbolos.tipo.Tipo;
import asem.tablaSimbolos.tipo.TipoMetodo;

public class NodoAsignacion extends NodoSentencia {
	protected NodoIDEncadenado ladoIzquierdo;
	protected NodoExpresion expresion;

	public NodoAsignacion(NodoIDEncadenado lista, NodoExpresion exp) {
		ladoIzquierdo = lista;
		expresion = exp;
	}

	public boolean chequeoSentencias(NodoBloque contenedor) {

		TipoMetodo tipoExpresion = expresion.chequeoSentencias(contenedor);

		Variable var = getVar(ladoIzquierdo.getIdentificador(), contenedor);

		Tipo tipoLadoIzquierdo = var.getTipo();

		if (ladoIzquierdo.getSiguiente() != null)
			tipoLadoIzquierdo = ladoIzquierdo.getSiguiente().chequeoSentencias(
					var);

		if (!tipoLadoIzquierdo.compatible(tipoExpresion))
			throw new ExcepcionTiposIncompatibles(
					ladoIzquierdo.getIdentificador(), tipoLadoIzquierdo,
					tipoExpresion);

		return false;
	}

	private Variable getVar(Token id, NodoBloque cont) {
		Variable v = cont.getVar(id.getLexema());
		Constructor met = TablaSimbolos.tabla.metodoActual();
		int offset;
		boolean noTieneSiguiente = (ladoIzquierdo.getSiguiente() == null);

		if (v != null) {

			if ((v instanceof Parametro)) {
				offset = 3 + met.getCantParametros() - v.getOffset();
				if (!met.esEstatico())
					offset++;

				if (noTieneSiguiente)
					GenC.gen.store(v, Integer.toString(offset));
				else
					GenC.gen.load(Integer.toString(offset),
							"Cargamos el valor del parametro " + v.getNombre());

				return v;
			}

			if (v instanceof VariableLocal) {
				offset = -v.getOffset();

				if (noTieneSiguiente)
					GenC.gen.store(v, Integer.toString(offset));
				else
					GenC.gen.load(
							Integer.toString(offset),
							"Cargamos el valor de la variable local "
									+ v.getNombre());

				return v;
			}

			if (v instanceof AtributoDeInstancia) {
				if (met instanceof Metodo)
					if (((Metodo) met).esEstatico())
						throw new ExcepcionAccesoInvalido(id, (Metodo) met,
								"atributo");
				if (noTieneSiguiente) {
					GenC.gen.loadThis();
					GenC.gen.swap("Swap del this");
					GenC.gen.storeref(Integer.toString(v.getOffset()),
							"Almaceno valor en la variable " + v.getNombre());
				} else
					GenC.gen.load(Integer.toString(v.getOffset()),
							"Cargamos el valor de la varinst " + v.getNombre());
			}
			return v;

		} else
			throw new ExcepcionEntidadNoDeclarada(id, TablaSimbolos.tabla
					.claseActual().getNombre(), "ninguno");

	}
}
